//---STD-------------------------------------------------------------
#include <cmath>

//---SM--------------------------------------------------------------
#include "MatriceGL.h"

//---Using-----------------------------------------------------------
using namespace SM;

//-------------------------------------------------------------------

MatriceGL::MatriceGL () : Matrice4<float>(false) {
}

MatriceGL::MatriceGL (float _fov, float _faspect, float _fnearplane, float _farplane) : Matrice4<float>(false) {
  setPerspective (_fov, _faspect, _fnearplane, _farplane) ;
}

MatriceGL::MatriceGL (const MatriceGL &_matrice) : Matrice4<float>((Matrice4<float>)_matrice) {
}

MatriceGL::~MatriceGL() {
}

void MatriceGL::setPerspective (float _fov, float _faspect, float _fnearplane, float _farplane) {
  this->init() ;

//   float fmaxy = _fnearplane * tanf (_fov * M_PI / 180.0f) ;
//   float fminy = -fmaxy ;
//   float fminx = fminy * _faspect ;
//   float fmaxx = fmaxy * _faspect ;



//   this->data[0] = (2.0f * _fnearplane) / (fmaxx - fminx) ;
//   this->data[5] = (2.0f * _fnearplane) / (fmaxy - fminy) ;
//   this->data[2] = (fmaxx + fminx) / (fmaxx - fminx) ;
//   this->data[6] = (fmaxy + fminy) / (fmaxy - fminy) ;
//   this->data[10] = - (_farplane + _fnearplane) / (_farplane - _fnearplane) ;
//   this->data[14] = -1.0f;
//   this->data[11] = -(2.0f * _farplane * _fnearplane) / (_farplane - _fnearplane) ;

//   this->data[0] = (2.0f * _fnearplane) / (fmaxx - fminx) ;
//   this->data[5] = (2.0f * _fnearplane) / (fmaxy - fminy) ;
//   this->data[8] = (fmaxx + fminx) / (fmaxx - fminx) ;
//   this->data[9] = (fmaxy + fminy) / (fmaxy - fminy) ;
//   this->data[10] = - (_farplane + _fnearplane) / (_farplane - _fnearplane) ;
//   this->data[11] = -1.0f;
//   this->data[14] = -(2.0f * _farplane * _fnearplane) / (_farplane - _fnearplane) ;
  const double PI = 3.14159265 ;
  float range = tan( ((_fov / 2.0f) * PI / 180.0f) ) * _fnearplane;
  float left = -range * _faspect;
  float right = range * _faspect;
  float bottom = -range;
  float top = range;
  data[0] = (2.0f * _fnearplane) / (right - left);
  data[5] = (2.0f * _fnearplane) / (top - bottom);
  data[10] = - (_farplane + _fnearplane) / (_farplane - _fnearplane);
  data[11] = - 1.0f ;
  data[14] = - (2.0f * _farplane * _fnearplane) / (_farplane - _fnearplane);
}

void MatriceGL::setLookAt (float _feyex, float _feyey, float _feyez,
                           float _fcamx, float _fcamy, float _fcamz,
                           float _fupx, float _fupy, float _fupz) {
  this->initIdentite() ;
  Matrice<float> camera (3, 1, false) ;
  camera[0] = _fcamx ;
  camera[1] = _fcamy ;
  camera[2] = _fcamz ;


  Matrice<float> eye (3, 1, false) ;
  eye[0] = _feyex ;
  eye[1] = _feyey ;
  eye[2] = _feyez ;

  Matrice<float> up (3, 1, false) ;
  up[0] = _fupx ;
  up[1] = _fupy ;
  up[2] = _fupz ;
  
  Matrice<float>  f(3, 1, false) ;
  Matrice<float>  u(3, 1, false) ;
  Matrice<float>  s(3, 1, false) ;
  f = camera - eye ;
  f.normalize() ;
  s.cross(f,up) ;
  s.normalize() ;
  u.cross(s, f) ;
  data[0] = s[0];
  data[4] = s[1] ;
  data[8] = s[2] ;
  data[1] = u[0] ;
  data[5] = u[1] ;
  data[9] = u[2] ;
  data[2] =-f[0] ;
  data[6] =-f[1] ;
  data[10] =-f[2] ;
  addTranslation(-_feyex, -_feyey, -_feyez)  ;
}

  void MatriceGL::push() {
    stackData.push(*this) ;
  }

  void MatriceGL::push(Matrice4<float> &_matrice) {
    stackData.push(_matrice) ;
  }

  void MatriceGL::pop() {
    Matrice4<float> *matThis = this ;
    *matThis = stackData.top() ;
    stackData.pop() ;
  }

